import asyncio
import logging
import uuid
from datetime import datetime, timedelta
from decimal import Decimal
from typing import Dict, Optional, List
from pydantic import BaseModel

from longport.openapi import (
    TradeContext,
    Order,
    OrderDetail,
    OrderSide,
    OrderType,
    Config,
    OrderStatus, TimeInForceType
    # StockPosition,
    # SubmitOrderResponse
)

from enum import Enum

from new_trade.db_toolkit import DatabaseManager


class PositionSide(Enum):
    LONG = "LONG"  # 多头持仓
    SHORT = "SHORT" # 空头持仓
    NET = "NET"     # 净持仓

# 配置日志
log_file = "order.log"
logger = logging.getLogger(__name__)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(log_file),
        logging.StreamHandler()
    ]
)

# ------------
# 数据模型定义
# ------------

class PositionData(BaseModel):
    """本地仓位数据结构"""
    symbol: str
    confirmed_long: int = 0  # 已确认多头数量
    confirmed_short: int = 0  # 已确认空头数量
    pending_long: int = 0  # 预扣多头数量
    pending_short: int = 0  # 预扣空头数量
    avg_price: float = 0.0  # 持仓均价
    last_sync: datetime = datetime.min  # 最后同步时间


class OrderRecord(BaseModel):
    """订单跟踪记录（修正版）"""
    symbol: str
    order_id: str
    side: OrderSide
    quantity: int
    status: OrderStatus.New  # 新增状态字段
    created_at: datetime = datetime.now()
    retries: int = 0
    filled_quantity: int = 0

    model_config = {
        "arbitrary_types_allowed": True
    }


class RiskConfig(BaseModel):
    """风险控制配置"""
    risk_per_trade: float = 0.01  # 单笔交易风险比例
    max_daily_risk: float = 0.03  # 单日最大风险
    position_ratio: float = 0.1  # 单标的最大仓位比例
    slippage_ratio: float = 0.001  # 滑点比例
    commission_ratio: float = 0.0002  # 手续费率
    order_timeout: int = 10  # 订单超时秒数


# ------------
# 订单管理系统
# ------------

class OrderManager:
    """专业级订单与仓位管理系统

    功能特性：
    1. 智能订单超时管理（30秒自动撤单）
    2. 本地仓位缓存与服务端同步
    3. 基于风险模型的头寸计算
    4. 多级异常处理与重试机制
    """

    def __init__(self, trade_ctx: TradeContext, config: RiskConfig):
        self.trade_ctx = trade_ctx  # 交易API上下文
        self.config = config  # 风控配置
        self.positions: Dict[str, PositionData] = {}  # 本地仓位缓存
        self.pending_orders: Dict[str, OrderRecord] = {}  # 进行中订单
        self.lock = asyncio.Lock()  # 线程安全锁
        # self.db = DatabaseManager(db_url)

        # 启动后台任务
        asyncio.create_task(self._monitor_orders())
        asyncio.create_task(self._sync_positions_loop())

    # ------------
    # 公共接口方法
    # ------------

    async def execute_signal(self, signal_type: str, symbol: str,
                             entry_price: float, stop_loss: Optional[float],
                             volatility: float, quantity: int=0) -> Optional[str]:
        """执行交易信号

        :param quantity:
        :param signal_type: 信号类型 (LONG/SHORT/CLOSE)
        :param symbol: 交易标的
        :param entry_price: 预期入场价格
        :param stop_loss: 止损价格 (可选)
        :param volatility: 当前波动率 (ATR或标准差)
        :return: 订单ID (失败返回None)
        """
        async with self.lock:
            try:
                # 1. 获取账户状态
                account = self.trade_ctx.account_balance()[0]
                if not quantity:
                # 2. 计算头寸规模
                    quantity = self._calculate_position_size(
                        symbol=symbol,
                        signal_type=signal_type,
                        entry_price=entry_price,
                        stop_loss=stop_loss,
                        volatility=volatility,
                        equity=float(account.net_assets)
                    )

                if quantity <= 0:
                    logger.warning(f"无效头寸数量 {symbol}: {quantity}")
                    return None

                # 3. 生成订单请求
                order_side = self._get_order_side(signal_type, symbol)
                limit_price = self._apply_slippage(order_side, entry_price)

                # # 记录信号
                # self.db.log_trade_signal({
                #     'signal_id': uuid.uuid4(),
                #     'symbol': symbol,
                #     'signal_type': signal_type,
                #     'entry_price': entry_price,
                #     'stop_loss': stop_loss,
                #     'volatility': volatility
                # })

                # 4. 提交订单
                order = self.trade_ctx.submit_order(
                    symbol=symbol,
                    side=order_side,
                    order_type=OrderType.LO,
                    submitted_quantity=Decimal(quantity),
                    submitted_price=Decimal(limit_price),
                    time_in_force=TimeInForceType.Day
                )

                # self.db.save_order({
                #     'order_id': order.order_id,
                #     'symbol': symbol,
                #     'side': order_side,
                #     'quantity': quantity,
                #     'status': "SUBMITTED",
                #     'created_at': datetime.now()
                # })

                # 5. 记录订单并预扣仓位
                self._track_order(order, quantity, signal_type, side=order_side)
                logger.info(f"订单提交成功 {order.order_id}")
                return order.order_id

            except Exception as e:
                logger.error(f"执行信号失败 {symbol}: {str(e)}", exc_info=True)
                # self.db.update_order_status(order.order_id, "FAILED")
                return None

    async def get_position(self, symbol: str, force_sync: bool = False) -> PositionData:
        """获取当前仓位（合并本地与服务端数据）"""
        async with self.lock:
            if force_sync or self._need_sync(symbol):
                await self._sync_position(symbol)
            return self.positions.get(symbol, PositionData(symbol=symbol))

    # ------------
    # 核心逻辑实现
    # ------------

    # def _calculate_position_size(self, symbol: str, signal_type: str, entry_price: float,
    #                              stop_loss: Optional[float], volatility: float, equity: float) -> int:
    #     """计算头寸规模"""
    #     # 获取当前仓位
    #     position = self.positions.get(symbol, PositionData(symbol=symbol))
    #
    #     # 可用风险计算
    #     used_risk = self._calculate_used_risk(position, entry_price, stop_loss)
    #     available_risk = min(
    #         self.config.risk_per_trade * equity,
    #         self.config.max_daily_risk * equity - self._today_total_risk()
    #     )
    #     available_risk = max(available_risk - used_risk, 0)
    #
    #     # 头寸计算逻辑
    #     if stop_loss:
    #         risk_per_share = abs(entry_price - stop_loss) * (1 + self.config.slippage_ratio)
    #     else:
    #         risk_per_share = volatility * 2  # 2倍波动率
    #
    #     risk_per_share += entry_price * self.config.commission_ratio  # 计入手续费
    #     quantity = available_risk / risk_per_share
    #
    #     # 应用限制条件
    #     max_quantity = (self.config.position_ratio * equity) / entry_price
    #     return int(min(quantity, max_quantity))

    def _calculate_position_size(self, symbol: str, signal_type: str, entry_price: float,
                                 stop_loss: Optional[float], volatility: float, equity: float) -> int:
        """计算头寸规模"""
        # 获取当前仓位
        position = self.positions.get(symbol, PositionData(symbol=symbol))

        # 可用风险计算
        used_risk = self._calculate_used_risk(position, entry_price, stop_loss)
        available_risk = min(
            self.config.risk_per_trade * equity,
            self.config.max_daily_risk * equity - self._today_total_risk()
        )
        available_risk = max(available_risk - used_risk, 0)

        # 头寸计算逻辑
        if stop_loss:
            risk_per_share = abs(entry_price - stop_loss) * (1 + self.config.slippage_ratio)
        else:
            risk_per_share = volatility * 2  # 2倍波动率

        risk_per_share += entry_price * self.config.commission_ratio  # 计入手续费
        quantity = available_risk / risk_per_share

        # 应用限制条件
        max_quantity = (self.config.position_ratio * equity) / entry_price
        return int(min(quantity, max_quantity))

    def _track_order(self, order, quantity: int, symbol: str, side: OrderSide):
        """记录订单并预扣仓位"""
        # 记录订单
        self.pending_orders[order.order_id] = OrderRecord(
            symbol=symbol,
            order_id=order.order_id,
            side=side,
            quantity=quantity,
            status=OrderStatus.New,
        )

        # 预扣仓位
        pos = self.positions.setdefault(symbol, PositionData(symbol=symbol))
        if side == "LONG":
            pos.pending_long += quantity
        elif side == "SHORT":
            pos.pending_short += quantity

    async def _sync_position(self, symbol: str):
        """同步单个标的仓位"""
        try:
            server_pos = self.trade_ctx.stock_positions([symbol,]).channels[0].positions[0]

            # 合并本地数据
            local_pos = self.positions.get(symbol, PositionData(symbol=symbol))
            merged = PositionData(
                symbol=symbol,
                confirmed_long=server_pos.quantity if server_pos.side == PositionSide.LONG else 0,
                confirmed_short=server_pos.quantity if server_pos.side == PositionSide.SHORT else 0,
                avg_price=float(server_pos.cost_price),
                last_sync=datetime.now()
            )

            # 保留预扣仓位
            merged.pending_long = local_pos.pending_long
            merged.pending_short = local_pos.pending_short

            self.positions[symbol] = merged

        except Exception as e:
            logger.error(f"仓位同步失败 {symbol}: {str(e)}")

    # ------------
    # 后台任务
    # ------------

    async def _monitor_orders(self):
        """订单监控后台任务"""
        while True:
            try:
                # 超时检查
                await self._check_timeouts()
                # 状态更新
                await self._refresh_order_statuses()
                # import pdb;pdb.set_trace()
                try:
                    await asyncio.sleep(1)  # 每秒运行一次
                except asyncio.CancelledError as e:
                    print(f"任务被取消: {e}")
                    import traceback
                    traceback.print_exc()
                    return  # 可以选择退出循环
            except Exception as e:
                logger.error(f"订单监控异常: {str(e)}", exc_info=True)
                await asyncio.sleep(5)

    async def _sync_positions_loop(self):
        """定期同步仓位"""
        while True:
            try:
                # 全量同步所有标的
                server_positions = self.trade_ctx.stock_positions().channels[0].positions
                if not server_positions:
                    logger.warning("未获取到任何仓位数据")
                    await asyncio.sleep(60)
                    continue
                for pos in server_positions:
                    async with self.lock:
                        self._merge_server_position(pos)
                # 每60秒同步一次
                await asyncio.sleep(60)
            except Exception as e:
                logger.error(f"全量同步失败: {str(e)}")
                await asyncio.sleep(10)

    # ------------
    # 工具方法
    # ------------

    def _get_order_side(self, signal_type: str, symbol: str) -> OrderSide:
        """转换信号类型到订单方向"""
        if signal_type == "CLOSE":
            position = self.positions.get(symbol)
            return OrderSide.SELL if position.confirmed_long > 0 else OrderSide.Buy
        return OrderSide.Buy if signal_type == "LONG" else OrderSide.Sell

    def _apply_slippage(self, side: OrderSide, price: float) -> float:
        """应用滑点到报价"""
        slippage = self.config.slippage_ratio
        return price * (1 + slippage) if side == OrderSide.Buy else price * (1 - slippage)

    async def _check_timeouts(self):
        logger.info("检查超时订单")
        """检查并处理超时订单"""
        now = datetime.now()
        to_cancel = []

        # 收集超时订单
        async with self.lock:
            for order_id, record in self.pending_orders.items():
                elapsed = (now - record.created_at).total_seconds()
                print(f"{order_id}: {elapsed:.2f}s")
                if elapsed > self.config.order_timeout:
                    to_cancel.append(order_id)

        # 批量撤单
        cancel_tasks = [self._safe_cancel(order_id) for order_id in to_cancel]

        if cancel_tasks:
            # import pdb;pdb.set_trace()
            try:
                await asyncio.gather(*cancel_tasks)
            except asyncio.CancelledError as e:
                print(f"批量撤单失败 {e}")

    async def _safe_cancel(self, order_id: str):
        """安全撤单操作"""
        try:
            print(f"检查订单 {order_id}")
            # 检查订单最新状态
            order_detail = self.trade_ctx.order_detail(order_id)

            if order_detail.status == OrderStatus.Filled:
                self._handle_filled_order(order_detail)
                return

            # 执行撤单
            self.trade_ctx.cancel_order(order_id)
            logger.info(f"订单超时撤单成功 {order_id}")

        except Exception as e:
            logger.error(f"撤单失败 {order_id}: {str(e)}")
            await self._retry_cancel(order_id)
        finally:
            async with self.lock:
                self._cleanup_order(order_id)

    async def _retry_cancel(self, order_id: str):
        """撤单重试机制"""
        record = self.pending_orders.get(order_id)
        if not record or record.retries >= 3:
            return

        record.retries += 1
        logger.warning(f"撤单重试 {order_id} 第{record.retries}次")
        await asyncio.sleep(2 ** record.retries)  # 指数退避
        await self._safe_cancel(order_id)

    def _cleanup_order(self, order_id: str):
        """清理订单记录"""
        if order_id in self.pending_orders:
            # 回滚预扣仓位
            record = self.pending_orders[order_id]
            pos = self.positions.get(record.symbol)
            if pos:
                if record.side == OrderSide.Buy:
                    pos.pending_long -= record.quantity
                else:
                    pos.pending_short -= record.quantity
            del self.pending_orders[order_id]

    async def _refresh_order_statuses(self):
        """主动刷新订单状态"""
        async with self.lock:
            # import pdb;pdb.set_trace()
            for order_id in self.pending_orders.keys():
                try:
                    order_detail = self.trade_ctx.order_detail(order_id)
                    if order_detail.status != self.pending_orders[order_id].status:
                        self._handle_order_update(order_detail)
                except Exception as e:
                    logger.error(f"订单状态刷新失败 {order_id}: {str(e)}")

    def _handle_order_update(self, order: OrderDetail):
        """处理订单状态更新"""
        if order.status == OrderStatus.Filled:
            self._handle_filled_order(order)

    def _handle_filled_order(self, order: OrderDetail):
        """处理成交订单"""
        with self.lock:
            pos = self.positions.get(order.symbol)
            if not pos:
                return

            filled_qty = order.executed_quantity
            if order.side == OrderSide.Buy:
                pos.confirmed_long += filled_qty
                pos.pending_long -= filled_qty
                # 更新平均成本
                pos.avg_price = (
                    (pos.avg_price * (pos.confirmed_long - filled_qty)) +
                    (filled_qty * order.price)
                ) / pos.confirmed_long
            else:
                pos.confirmed_short += filled_qty
                pos.pending_short -= filled_qty
                pos.avg_price = (
                    (pos.avg_price * (pos.confirmed_short - filled_qty)) +
                    (filled_qty * order.price)
                ) / pos.confirmed_short

            # 清理已完成订单
            if order.order_id in self.pending_orders:
                del self.pending_orders[order.order_id]

    def _need_sync(self, symbol: str) -> bool:
        """判断是否需要同步仓位"""
        pos = self.positions.get(symbol)
        if not pos:
            return True
        return (datetime.now() - pos.last_sync).total_seconds() > 300  # 5分钟强制同步

    def _today_total_risk(self) -> float:
        """计算当日累计风险（需实现实际逻辑）"""
        # 示例：从数据库或本地缓存获取
        return 0.0

    def _calculate_used_risk(self, position: PositionData,
                             entry_price: float, stop_loss: Optional[float]) -> float:
        """计算已占用风险"""
        # 示例逻辑：根据现有仓位计算已用风险
        return 0.0

    def _merge_server_position(self, server_pos):
        """合并服务端仓位到本地缓存"""
        symbol = server_pos.symbol

        # 获取本地仓位记录（不存在则创建）
        local_pos = self.positions.setdefault(
            symbol,
            PositionData(symbol=symbol)
        )

        # 保留本地预扣仓位
        pending_long = local_pos.pending_long
        pending_short = local_pos.pending_short

        # 更新已确认仓位
        new_confirmed_long = server_pos.quantity if server_pos.quantity > 0 else 0
        new_confirmed_short = server_pos.quantity if server_pos.quantity < 0 else 0

        # 合并逻辑
        self.positions[symbol] = PositionData(
            symbol=symbol,
            confirmed_long=new_confirmed_long,
            confirmed_short=new_confirmed_short,
            pending_long=pending_long,
            pending_short=pending_short,
            avg_price=float(server_pos.cost_price),
            last_sync=datetime.now()
        )
        # logger.info(f"仓位更新 {symbol}: {local_pos} -> {self.positions[symbol]}")

        # # 对冲检测
        # if self._check_hedge(symbol):
        #     logger.warning(f"仓位对冲风险 {symbol}")
        #     self._alert_risk_management(symbol)
        #
        # def _check_hedge(self, symbol: str) -> bool:
        #     """检测多头空头对冲风险"""
        #     pos = self.positions.get(symbol)
        #     if not pos:
        #         return False
        #
        #     return (pos.confirmed_long > 0 and pos.confirmed_short > 0) or \
        #         (pos.confirmed_long + pos.pending_long < pos.confirmed_short) or \
        #         (pos.confirmed_short + pos.pending_short < pos.confirmed_long)


# ------------
# 使用示例
# ------------

async def main():
    # 初始化交易上下文
    config = Config.from_env()
    trade_ctx = TradeContext(config)

    # 风险配置
    risk_config = RiskConfig(
        risk_per_trade=0.01,
        max_daily_risk=0.03,
        position_ratio=0.1,
        slippage_ratio=0.001,
        commission_ratio=0.0002,
        order_timeout=10
    )

    # 创建订单管理器
    order_mgr = OrderManager(trade_ctx, risk_config)

    # 执行买入信号
    await order_mgr.execute_signal(
        signal_type="LONG",
        symbol="AAPL.US",
        entry_price=150.0,
        stop_loss=145.0,
        volatility=2.5
    )
    while True:
        await asyncio.sleep(1)


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    asyncio.run(main())